<?php
   //                                                                                   C
  //                                                                                \/ |\/| |
 /////////////////////////////////////////////////////////////\                     /\ |  | |__
//                                                           //\                        S
//     License:                                              // \      /\                I 
//     This is free open source software,                    //  \    /  \                M Y
//     There is no warranty on it,                           //   \  /    \                P
//     It is delviered as it is,                             //    \/      \                L                        /\
//     You can modify it to tailor your needs,               //             \                E                      /  \      !
//     You can change license of your copy as you wish.      //              \                ________version_0.1__/    \    /
//     Copyleft: supertramp@wasthere.com                     //               \              /                           \  / 
//                                                           //                \            /                             \/
//////////////////////////////////////////////////////////////                  \          / 
//                                                                               \  /\    /
// config constants:                                                              \/  \  / 
//                                                                                     \/
define('SERVER_SIDE_CONTENT_DIR', dirname(__FILE__).'/data/');
define('ADD_NEW_CAPTION', 'Add new');
define('SAVE_CHANGES_CAPTION', 'Save changes');
define('UNIQUE_IDENTIFIER_CAPTION', '* Unique identifier');
define('CAPTION_ARE_YOU_SURE', 'Are you sure to delete this?');
define('CAPTION_DELETE', ' (delete) ');
define('CAPTION_EDIT', ' (edit) ');
define('ADD_PARAM', 'hmai723d8nh2mdfy');
define('DEST_PARAM', 'gnmud83uyn82e');
define('LABEL_PARAM', 'm2dy8n28nf2nnwe');
define('DELETE_PARAM', 'jn287yn8ey7ewnw');
//
class MySimpleXmlCms{
//
 //
  // returns panel to manage content 
  // from XML files located in $this->sServerSideContentDir directory
  public function getPanel($aModulesArray){
    if(isset($_GET[ADD_PARAM])){
      return $this->getForm($_GET[ADD_PARAM], $_GET[DEST_PARAM], $_GET[ADD_PARAM] == $_GET[DEST_PARAM]);
    } else {
      foreach($aModulesArray as $sHeader => $aAccess){
        $sXmlFileNamePattern = $aAccess[0];
        $sAddTag = '';
        $bDelOption = false;
        if(count($aAccess) == 2){
          $sUrl = 'index.php?'.ADD_PARAM.'='.$aAccess[1].'&amp;'.DEST_PARAM.'='.str_replace("*",time(),$sXmlFileNamePattern);
          $sAddTag = '<a href="'.$sUrl.'">'.ADD_NEW_CAPTION.'</a>';
          $bDelOption = true;
        }
        $sReturn .= '<h2>'.$sHeader.$sAddTag.'</h2>';
        $sReturn .= $this->getControlPanel($sXmlFileNamePattern, $bDelOption);
      }
      return $sReturn;
    }
  }
  //
  // saves POST data to XML files in definied directory
  public function __construct($sServerSideContentDir = SERVER_SIDE_CONTENT_DIR){
    $this->sServerSideContentDir = $sServerSideContentDir;
    if(isset($_POST[ADD_PARAM])){
      $this->addNewXml($_POST[ADD_PARAM], $_POST[DEST_PARAM]);
    } else if(isset($_GET[DELETE_PARAM])){
      $this->deleteExistingXml($_GET[DELETE_PARAM]);
    }
  }
  //
  private function addNewXml($sFileName, $sExistingSchemaPattern){
    $aData = $_POST['data'];
    $xDoc = new SimpleXMLElement(file_get_contents($this->sServerSideContentDir.$sExistingSchemaPattern));
    $xDoc["label"] = $_POST[LABEL_PARAM];
    $xDoc = $this->updateAllElems($xDoc, $aData, '/data');
    $xDoc->asXml($this->sServerSideContentDir.$sFileName);
  }
  //
  private function updateAllElems($xDoc, $aData, $sXPath){
    foreach($aData as $sKey => $mElem){
      if(is_array($mElem)){
        $xDoc = $this->updateAllElems($xDoc, $mElem, $sXPath."/content[@key='$sKey']");
      } else {
        $xRes = $xDoc->xpath($sXPath."/content[@key='$sKey']");
        $xRes[0][0] = $mElem;
      }
    }
    return $xDoc;
  }
  //
  private function deleteExistingXml($sFileName){
    if(strpos($sFileName, '/') === false){
      rename($this->sServerSideContentDir.$sFileName, $this->sServerSideContentDir.time().'-'.$sFileName);
    }
  }
  //
  private function getControlPanel($sXmlFileNamePattern, $bDelOption){
    $aSortedData = glob($this->sServerSideContentDir.$sXmlFileNamePattern);
    usort($aSortedData, "MySimpleXmlCms::sort");
    $sReturn = '<ol>';
    foreach($aSortedData as $sFilePath){
      $sFileName = substr($sFilePath, strrpos($sFilePath, '/') + 1);
      $xDoc = new SimpleXMLElement(file_get_contents($sFilePath));
      $sLabel = (String)$xDoc["label"];
      $sReturn .= '<li>';
      $sUrl = 'index.php?'.ADD_PARAM.'='.$sFileName.'&amp;'.DEST_PARAM.'='.$sFileName;
      $sReturn .= '<a href="'.$sUrl.'">'.$sLabel.CAPTION_EDIT.'</a>';
      if($bDelOption){
        $sOnClick = 'return confirm(\''.CAPTION_ARE_YOU_SURE.'\');';
        $sUrl = 'index.php?'.DELETE_PARAM.'='.$sFileName;
        $sReturn .= '<a onclick="'.$sOnClick.'" href="'.$sUrl.'">'.CAPTION_DELETE.'</a>';
      }
      $sReturn .= '</li>';
    }
    $sReturn .= '</ol>';
    return $sReturn;
  }
  //
  // returns array with data from given XML file 
  // to use this data to display content on end-user page
  public function getData($sXmlFileName){
    return $this->getAllElems(new SimpleXMLElement(file_get_contents($this->sServerSideContentDir.$sXmlFileName)));
  }
  //
  public static function sort($sFilePath1, $sFilePath2){
    $xDoc1 = new SimpleXMLElement(file_get_contents($sFilePath1));
    $xDoc2 = new SimpleXMLElement(file_get_contents($sFilePath2));
    return ((String)$xDoc1["label"]) > ((String)$xDoc2["label"]);
  }
  //
  // returns data from multiple files merged to one array, 
  // each file data in separate (sub)array
  // to use this data to display content on end-user page
  public function getMergedData($sXmlFileNamePattern){
    $aReturn = array();
    $aSortedData = glob($this->sServerSideContentDir.$sXmlFileNamePattern);
    usort($aSortedData, "MySimpleXmlCms::sort");
    foreach($aSortedData as $sFilePath){
      $xDoc = new SimpleXMLElement(file_get_contents($sFilePath));
      $aReturn[(String)$xDoc["label"]] = $this->getAllElems($xDoc);
    }
    return $aReturn;
  }
  //
  // recursively load data from SimpleXMLElement to an array
  private function getAllElems($xDoc){
    $aReturn = array();
    foreach($xDoc as $xElem){
      if($xElem["type"] == "array"){
        $aReturn[(String)$xElem["key"]] = $this->getAllElems($xElem);
      } else {
        $aReturn[(String)$xElem["key"]] = stripslashes((String)$xElem);
      }
    }
    return $aReturn;
  }
  //
  // get html5 form to edit data in given XML file
  private function getForm($sXmlFileName, $sDestinationFile, $bModifyOnly){
    $xDoc = new SimpleXMLElement(file_get_contents($this->sServerSideContentDir.$sXmlFileName));
    $sInitLabel = $bModifyOnly ? ((String)$xDoc["label"]) : '';
    $sCaption = $bModifyOnly ? SAVE_CHANGES_CAPTION : ADD_NEW_CAPTION;
    $sReturn .= '<form id="'.$sXmlFileName.'" class="'.((String)$xDoc["key"]).'" action="./" method="post">';
    $sReturn .= '<h2>'.((String)$xDoc["label"]).'</h2>';
    $sReturn .= '<input type="hidden" name="'.DEST_PARAM.'" value="'.$sXmlFileName.'" />';
    $sReturn .= '<input class="'.((String)$xDoc["key"]).' top" type="submit" value="'.$sCaption.'" title="'.$sCaption.'" />';
    $sReturn .= '<input type="hidden" value="'.$sDestinationFile.'" name="'.ADD_PARAM.'" />';
    $sReturn .= '<ol>';
    $sReturn .= '<li class="'.((String)$xDoc["key"]).'">';
    $sReturn .= '<p><b>'.UNIQUE_IDENTIFIER_CAPTION.'</b></p>';
    $sReturn .= '<input value="'.$sInitLabel.'" required="required" type="text" name="'.LABEL_PARAM.'" />';
    $sReturn .= '</li>';
    $sReturn .= $this->getAllFields($xDoc, 'data');
    $sReturn .= '</ol>';
    $sReturn .= '<input class="'.((String)$xDoc["key"]).' bottom" type="submit" value="'.$sCaption.'" title="'.$sCaption.'" />';
    $sReturn .= '</form>';
    return $sReturn;
  }
  //
  // recursively load data from SimpleXMLElement to form fields
  private function getAllFields($xDoc, $sCurrentArrayPath){
    foreach($xDoc as $xElem){
      $sReturn .= '<li class="'.$sCurrentArrayPath.'['.((String)$xElem["key"]).']'.'">';
      switch($xElem["type"]){
      case 'array':
        $sReturn .= '<h3>'.((String)$xElem["label"]).':</h3>';
        $sReturn .= '<ul class="'.$sCurrentArrayPath.'['.((String)$xElem["key"]).']'.'">';
        $sReturn .= $this->getAllFields($xElem, $sCurrentArrayPath.'['.((String)$xElem["key"]).']');
        $sReturn .= '</ul>';
        break;
      case 'textarea':
        $sReturn .= '<p>'.((String)$xElem["label"]).'</p>';
        $sReturn .= '<textarea name="'.$sCurrentArrayPath.'['.((String)$xElem["key"]).']'.'">'.stripslashes((String)$xElem).'</textarea>';
        break;
      default:
        $sReturn .= '<p>'.((String)$xElem["label"]).'</p>';
        $sReturn .= '<input type="text" name="'.$sCurrentArrayPath.'['.((String)$xElem["key"]).']'.'" value="'.stripslashes((String)$xElem).'" />';
        break;
      }
      $sReturn .= '</li>';
    }
    return $sReturn;
  }
}
?>